
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using ZuDoKu.Screens;
#endregion

namespace ZuDoKu
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public sealed class MainGame : Microsoft.Xna.Framework.Game
    {
        public const int Width = 240;
        public const int Height = 320;

        private SpriteFont _defaultFont;
        private static MainGame _instance;
        GraphicsDeviceManager _graphics;
        SpriteBatch _spriteBatch;
        Screen _screen;

        public static MainGame Instance
        {
            get { return _instance; }
        }

        public SpriteBatch SpriteBatch
        {
            get { return _spriteBatch; }
        }

        /// <summary>
        /// Initialize the game. 
        /// </summary>
        /// <param name="startScreen">The first screen to show when the game starts.</param>
        public MainGame()
        {
            _instance = this;
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

#if WINDOWS
            _graphics.PreferredBackBufferWidth = Width;
            _graphics.PreferredBackBufferHeight = Height;
#endif
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            _screen = new Splash();
            _defaultFont = Content.Load<SpriteFont>(@"Fonts\Arial");

            lock (_screen)
            {
                _screen.Change += new EventHandler<ScreenChangeEventArgs>(screen_Change);

                Components.Add(_screen);

                base.Initialize();
            }
        }

        /// <summary>
        /// The default Font for drawing text.
        /// </summary>
        public SpriteFont DefaultFont
        {
            get { return _defaultFont; }
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            base.LoadContent();
        }

        protected override void Update(GameTime gameTime)
        {
            lock (_screen)
            {
                base.Update(gameTime);
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            _graphics.GraphicsDevice.Clear(Color.Black);

            lock (_screen)
            {
                base.Draw(gameTime);
            }
        }

        private DateTime _lastScreenChanged = DateTime.MinValue;
        /// <summary>
        /// This controls the game flow. Screens tell the main game which screen to proceed to. If the
        /// new screen is null then the game should exit. Initialize is called here, do not call initialize 
        /// manually.
        /// </summary>
        /// <param name="sender">The screen sending this event.</param>
        /// <param name="e">Arguments telling which new screen to go to.</param>
        private void screen_Change(object sender, ScreenChangeEventArgs e)
        {
            lock(_screen)
            {
                using (_screen)
                {
                    // Throttle this down so one button press doesn't unload the whole app.
                    if (_lastScreenChanged.AddMilliseconds(200) < DateTime.Now)
                    {
                        Components.Remove(_screen);
                        _screen.Change -= new EventHandler<ScreenChangeEventArgs>(screen_Change);

                        if (e.Screen == null)
                        {
                            this.Exit();
                        }
                        else
                        {
                            _screen = e.Screen;
                            _screen.Change += new EventHandler<ScreenChangeEventArgs>(screen_Change);
                            Components.Add(_screen);
                        }
                    }
                }
            }
        }
    }
}